package com.xique.door.util;

import com.xique.common.core.domain.R;
import com.xique.common.core.utils.StringUtils;
import com.xique.common.core.utils.json.JSONObject;
import com.xique.common.core.web.domain.AjaxResult;
import com.xique.common.redis.service.RedisService;
import com.xique.door.project.miniprogram.api.IWxClientApi;
import com.xique.door.project.miniprogram.bean.domian.WeixinCode2Session;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author shd
 * @create 2022/4/13 20:29
 */
@Component
@Slf4j
public class WechatUtils {

    @Autowired
    private IWxClientApi wxClientApi;

    @Autowired
    private RedisService redisService;

    private final static String PREFIX = "wx:";

    /**
     * 验证签名的方法
     * token
     * @param
     * @param signature 微信加密签名
     * @param timestamp 时间戳
     * @param nonce 随机数
     * @return 将sha1加密后的字符串可与signature对比，返回boolean
     */
    public boolean checkSignature(String signature, String token, String timestamp, String nonce) {
        //将token、timestamp、nonce三个参数进行字典序排序
        String[] strs = new String[] {token, timestamp, nonce};
        Arrays.sort(strs);
        //将三个参数字符串拼接成一个字符串进行sha1加密
        String str = strs[0]+strs[1]+strs[2];
        String mysig = sha1(str);

        //判断加密后的mysig和微信发送得的signature是否相等,返回boolean
        return mysig.equals(signature);
    }

    /**
     * 进行sha1加密
     * @param str
     * @return
     */
    private String sha1(String str) {
        try {
            //获取一个加密对象
            MessageDigest md = MessageDigest.getInstance("sha1");
            //加密
            byte[] digest = md.digest(str.getBytes());
            //处理加密结果
            char[] chars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
            StringBuilder sb = new StringBuilder();
            //处理加密结果
            for (byte b : digest) {
                sb.append(chars[(b>>4)&15]);
                sb.append(chars[b&15]);
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

    private R<String> getAccessToken(String appId, String secret) {

        String accessToken = redisService.getCacheObject(PREFIX + appId);

        if (StringUtils.isNotEmpty(accessToken)) {
            return R.ok(accessToken);
        }

        Map<String, Object> params = new HashMap<>(3);
        params.put("appid", appId);
        params.put("secret", secret);
        params.put("grant_type", "client_credential");
        log.info("access_token请求参数：{}", params);

        JSONObject result = wxClientApi.getAccessToken(params);
        log.info("access_token返回结果：{}", result);

        if (StringUtils.isEmpty(result)) {
            return R.fail("微信未返回消息");
        }

        if (StringUtils.isNotNull(result.getInt("errcode")) && 0 != result.getInt("errcode")) {
            return R.fail(result.getStr("errmsg"));
        }

        accessToken = result.getStr("access_token");
        Long expiresIn = result.getLong("expires_in");

        redisService.setCacheObject(PREFIX + appId, accessToken, expiresIn, TimeUnit.SECONDS);

        return R.ok(accessToken);
    }

    public R<String> getPhone(String appId, String secret, String code) {
        if (StringUtils.isEmpty(code)) {
            return R.fail("code不能为空");
        }
        R<String> result = getAccessToken(appId, secret);
        if (R.FAIL == result.getCode()) {
            return R.fail(result.getMsg());
        }
        Map<String, Object> params = new HashMap<>(2);
        params.put("access_token", result.getData());
        params.put("code", code);
        log.info("getPhone请求参数：{}", params);

        JSONObject wxResult = wxClientApi.getPhone(Collections.singletonMap("access_token", result.getData()), Collections.singletonMap("code", code));
        log.info("getPhone返回结果：{}", wxResult);

        if (StringUtils.isEmpty(wxResult)) {
            return R.fail("微信未返回消息");
        }

        if (StringUtils.isNotNull(wxResult.getInt("errcode")) && 0 != wxResult.getInt("errcode")) {
            return R.fail(wxResult.getStr("errmsg"));
        }

        JSONObject phoneInfo = wxResult.getObj("phone_info");
        String phoneNumber = phoneInfo.getStr("phoneNumber");

        return R.ok(phoneNumber);
    }

    public R<WeixinCode2Session> getCode2Session(String appId, String secret, String code) {
        if (StringUtils.isEmpty(code)) {
            return R.fail("code不能为空");
        }
        Map<String, Object> params = new HashMap<>(4);
        params.put("appid", appId);
        params.put("secret", secret);
        params.put("js_code", code);
        params.put("grant_type", "authorization_code");
        log.info("jscode2Session请求参数：{}", params);

        JSONObject result = wxClientApi.jscode2session(params);
        log.info("jscode2Session返回结果：{}", result);

        if (StringUtils.isEmpty(result)) {
            return R.fail("微信未返回消息");
        }

        if (StringUtils.isNotNull(result.getInt("errcode")) && 0 != result.getInt("errcode")) {
            return R.fail(result.getStr("errmsg"));
        }

        WeixinCode2Session session = new WeixinCode2Session();
        session.setOpenId(result.getStr("openid"));
        session.setSessionKey(result.getStr("session_key"));
        session.setUnionId(result.getStr("unionid"));

        return R.ok(session);
    }

    public AjaxResult sendMessage(String appId, String secret, String touser, String templateId,
                                  String url, Map<String, Object> miniprogram,
                                  com.alibaba.fastjson.JSONObject data) {
        R<String> result = getAccessToken(appId, secret);
        if (R.FAIL == result.getCode()) {
            return AjaxResult.error(result.getMsg());
        }
        Map<String, Object> params = new HashMap<>(3);
        params.put("touser", touser);
        params.put("template_id", templateId);
        params.put("url", url);
        params.put("miniprogram", miniprogram);
        params.put("data", data);
        log.info("订阅消息推送参数：{}", params);

        JSONObject wxResult = wxClientApi.bizsend(Collections.singletonMap("access_token", result.getData()), params);
        log.info("订阅消息推送结果：{}", wxResult);

        if (StringUtils.isEmpty(wxResult)) {
            return AjaxResult.error("微信未返回消息");
        }

        if (StringUtils.isNotNull(wxResult.getInt("errcode")) && 0 != wxResult.getInt("errcode")) {
            return AjaxResult.error(JSONObject.valueAsStr(wxResult));
        }

        return AjaxResult.success();
    }

    public JSONObject getGzhOpenIds(String appId, String secret, String nextOpenId) {
        R<String> result = getAccessToken(appId, secret);
        if (R.FAIL == result.getCode()) {
            return null;
        }
        Map<String, Object> params = new HashMap<>(3);
        params.put("access_token", result.getData());
        params.put("next_openid", nextOpenId);
        log.info("获取用户列表参数：{}", params);

        JSONObject wxResult = wxClientApi.getUserList(params);
        log.info("获取用户列表结果：{}", wxResult);
        return wxResult;
    }

    public JSONObject getGzhUserInfo(String appId, String secret, String openId) {
        R<String> result = getAccessToken(appId, secret);
        if (R.FAIL == result.getCode()) {
            return null;
        }
        Map<String, Object> params = new HashMap<>(3);
        params.put("access_token", result.getData());
        params.put("openid", openId);
        log.info("获取用户信息参数：{}", params);

        JSONObject wxResult = wxClientApi.getUserInfo(params);
        log.info("获取用户信息结果：{}", wxResult);
        return wxResult;
    }
}
